package visualizer.controller;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import processing.core.PApplet;

import visualizer.data.DataChangeListener;
import visualizer.drawable.DrawableSwitcher;
import visualizer.google.concrete.GlobalAnalyzer;

public class GlobalAnalyzeExecutor {
	private GlobalAnalyzer ga;
	private PApplet app;
	private Executor fetcher = Executors.newSingleThreadExecutor();

	private static final int leftColor = 0xFFEAFD3D;
	private static final int rightColor = 0xFFFF0303;
	private static final int baseColor = 0xFFFFFFFF;
	
	public GlobalAnalyzeExecutor(GlobalAnalyzer ga, PApplet app)
	{
		this.ga = ga;
		this.app = app;
	}

	public void attach(final InputController input, final DrawableSwitcher<String> viewSwitcher) {
		input.getCountryColors().addListener(new DataChangeListener<Map<String,Integer>>() {
			public void dataChanged(Map<String, Integer> from, Map<String, Integer> to) {
				viewSwitcher.setSelectedKey("countries");
			}
		});
		
		input.getSubmitButtonInput().addListener(new DataChangeListener<Integer>() {
			public void dataChanged(Integer from, Integer to) {
				String left = input.getLeftSearchTermInput().get();
				String right = input.getRightSearchTermInput().get(); 
				if ((left.length() > 0)
					&& (right.length() > 0)) {
					viewSwitcher.setSelectedKey("loading");
					fetcher.execute(getGlobalAnalyzationRunnable(input, left, right));
				}
			}
		});
	}
	
	public Runnable getGlobalAnalyzationRunnable(final InputController input, final String left, final String right)
	{
		return new Runnable() {
			public void run() {
				Map<String,Integer> leftResult = ga.getEstimatedHitsPerCountry(left);
				Map<String,Integer> rightResult = ga.getEstimatedHitsPerCountry(right);
				setCountryColors(input, leftResult, rightResult);
			}
		};
	}
	
	public int getMaximumValue(Collection<Integer> values)
	{
		int maximum = 0;
		
		for (int i : values) {
			if (i > maximum) {
				maximum = i;
			}
		}
		
		return maximum;
	}
	
	public void setCountryColors(InputController input, Map<String,Integer> left, Map<String,Integer> right)
	{
		int maximum = Math.max(getMaximumValue(left.values()), getMaximumValue(right.values()));
		
		Map<String,Integer> result = new LinkedHashMap<String, Integer>();
		for (Map.Entry<String, Integer> e : left.entrySet()) {
			int leftValue = e.getValue();
			int rightValue = right.get(e.getKey());
			
			if (leftValue < 1) {
				leftValue = 1;
			}
			
			if (rightValue < 1) {
				rightValue = 1;
			}
			
			double leftRelative = (double) leftValue / maximum;
			double rightRelative = (double) rightValue / maximum;
			
			double bias = rightRelative / (leftRelative + rightRelative);
			double richness = Math.log((leftValue + rightValue) / 2) / Math.log(maximum);
			int averageColor = blend(leftColor, rightColor, bias, richness);
			result.put(e.getKey(), averageColor);
		}
		input.getCountryColors().set(result);
	}
	
	public int blend(int left, int right, double bias, double richness)
	{
		int biased = getWeightedAverageByComponent(left, right, bias);
		int lighted = getWeightedAverageByComponent(baseColor, biased, richness);
		return lighted;
	}
	
	public int getWeightedAverageByComponent(int left, int right, double weight)
	{
		int[] leftComponents = getComponents(left);
		int[] rightComponents = getComponents(right);
		int[] output = new int[4];
		
		for (int i = 0; i < 4; i++) {
			output[i] = leftComponents[i] + (int) ((rightComponents[i] - leftComponents[i]) * weight);
		}
		
		return getColorByComponents(output);
	}
		
	public int[] getComponents(int color)
	{
		return new int[] { (color >> 24) & 255, (color >> 16) & 255, (color >> 8) & 255, color & 255 };
	}
	
	public int getColorByComponents(int[] components)
	{
		return components[0] << 24 | components[1] << 16 | components[2] << 8 | components[3];
	}
}
